#include "Utils.hpp"

namespace htmk 
{
	namespace utils 
	{
		bool Split(CoChar_t& sep, CoCStr_t raw, VecStr_t& split_values)
		{
			Str_t line(raw);
			size_t pos = 0;
			while((pos = line.find_first_of(sep)) != std::string::npos)
			{
                if (pos > 0)
                {
                    Str_t extr = line.substr(0, pos);
                    if (extr.size() > 0)
                        split_values.push_back(extr);
                }
				line = line.substr(pos+1, line.size());
			}
            if (line.size() > 0)
                split_values.push_back(line);
			if (split_values.size() == 0) return false;

			return true;
		}

		Str_t CurrentDir(void)
		{
			char rawDir[FILENAME_MAX];
			GetCurrentDir(rawDir, FILENAME_MAX);			
			return std::string(rawDir);
		}

		size_t NumOfDirLevels(void)
		{
			VecStr_t dirs;
			Split(GetDirSep(), CurrentDir().c_str(), dirs);
			return dirs.size();
		}
        
        size_t NumOfDirLevels(CoCStr_t& path)
		{
			VecStr_t dirs;
			Split(GetDirSep(), path, dirs);
			return dirs.size();
		}
        

		Str_t DirLevel(CInt_t& Level)
		{
			VecStr_t dirs;
			Str_t Dir;
			Split(GetDirSep(), CurrentDir().c_str(), dirs);
			if (Level > 0 && Level < dirs.size())			
			{
				Dir.clear();
				size_t i = 0;
				for(VecStr_t::iterator it = dirs.begin(); i < Level && it != dirs.end(); it++, i++)				
				{
					if (it == dirs.begin())
                    {
                        if (GetDirSep() == '/')
                            Dir.append(1, '/');
						Dir.append(*it);
                    }
					else
						Dir.append(GetDirSep() + *it);				
				}
			}
			return Dir;
		}
	
		Str_t DirLevel(CInt_t& Level, CoStr_t& path)
		{
			VecStr_t dirs;
			Str_t Dir;
			Split(GetDirSep(), path.c_str(), dirs);
#           ifndef WIN
            if (dirs.size() == 1)
                return Str_t("/");
#           endif
			if (Level > 0 && Level < dirs.size())
			{
				Dir.clear();
				size_t i = 0;
                
				for(VecStr_t::iterator it = dirs.begin(); i < Level && it != dirs.end(); it++, i++)				
				{
					if (it == dirs.begin())
                    {
                        if (GetDirSep() == '/')
                            Dir.append(1, '/');
						Dir.append(*it);
                    }
					else
						Dir.append(GetDirSep() + *it);				
				}
			}
			return Dir;
		}

		Char_t GetDirSep(void)
		{			
			Str_t like_unix = CurrentDir().substr(0, 1);
			Str_t like_win  = CurrentDir().substr(1, 2);

			if ( like_unix.compare("/") == 0 )
				return '/';
			else if ( like_win.compare(":\\") == 0 ) 
				return '\\';
			
			return '#';

		}

		Str_t FixWinPath(CoStr_t& path)
		{
			size_t len = path.size();
			if (len == 0) return "";
			Str_t n_path;
			n_path.resize(len+1);
			char* c_str = new char[len+1];		
			memset(c_str, 0, len+1);
			strncpy(c_str, path.c_str(), len);
			size_t x = 0;
			for(size_t i = 0; i < len; i++)
			{
				if (*(c_str+i) == '\\' && *(c_str+i+1) != '\\')
				{
					n_path.resize(n_path.size()+1);
					n_path[x++] = '\\';
				}
				n_path[x++] = *(c_str+i);
			}
			delete[] c_str;
			c_str = NULL;

			return n_path;
		}

		Str_t ExtractEXTENSION(CoStr_t& path)
		{
			return path.substr(path.find_last_of(".")+1, path.size());
		}

		bool ListFiles(CoCStr_t path, CoChar_t tipo, Vfile_t& files)
		{
			Str_t spath;
			if (tipo == 'R')
            {
                int level = NumOfDirLevels(path);
                if (level - 1 == 0)
                    level = 1;
                else if (level - 1 < 0)
                    goto list;
                else
                    level--;
				spath = DirLevel(level, path);
            }
        list:
			#ifdef WIN
                return ListFilesWIN((spath.empty())?path:spath.c_str(), files);
            #else
                return ListFilesUNIX((spath.empty())?path:spath.c_str(), files);
			#endif
		}
        
#ifdef WIN
		bool ListFilesWIN(CoCStr_t path, Vfile_t& files)
		{
			HANDLE hFind;
			WIN32_FIND_DATA data;
			
			// I - char* TO wchar_t* <== IN(path)
			size_t newsize = strlen(path) + 20;
			wchar_t * wcstring = new wchar_t[newsize];
			size_t convertedChars = 0;
			mbstowcs_s(&convertedChars, wcstring, newsize, path, _TRUNCATE);
			if ( convertedChars == 0 ) return false;
			newsize = convertedChars = 0;
			// F - char* TO wchar_t* ==> OUT(wcstring)

			StringCchCat(wcstring, MAX_PATH, TEXT("\\*"));
			hFind = FindFirstFile(wcstring, &data);
			if (hFind != INVALID_HANDLE_VALUE) 
			{
				char *nstring = NULL;
				size_t origsize = 0;
				newsize = 0;
				file_t hfile;
				do 
				{
					// I - wchar_t* TO char* <== IN(data.cFileName)
					origsize = wcslen(data.cFileName) + 20;						
					newsize = origsize * 2;								
					nstring = new char[newsize];
					wcstombs_s(&convertedChars, nstring, newsize, data.cFileName, _TRUNCATE);
					if ( convertedChars == 0 ) return false;
					// F - wchar_t* TO char* ==> OUT(nstring)
					
					if (data.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
						hfile.tipo = "F";
					else if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
						hfile.tipo = "D";
					hfile.name = nstring;
					if (hfile.name.compare(".") == 0)
						goto clear;
                    if (hfile.name.compare("..") != 0 && hfile.name[0] == '.')
                        goto clear;
					hfile.ruta = path;
					if (hfile.name.compare("..") != 0)
					{
						Char_t lastc = hfile.ruta[hfile.ruta.size()-1];
						if (lastc != htmk::utils::GetDirSep())
							hfile.ruta.append(1, htmk::utils::GetDirSep());
						hfile.ruta.append(hfile.name);
						lastc = '\0';
						lastc = hfile.ruta[hfile.ruta.size()-1];
						if (lastc != htmk::utils::GetDirSep() && hfile.tipo.compare("D") == 0)
							hfile.ruta.append(1, htmk::utils::GetDirSep());
					}
                    else if (hfile.name.compare("..") == 0)
                    {
                        hfile.tipo = "R";
                    }
					files.push_back(hfile);
					clear:
						if (nstring) { delete[] nstring; nstring = NULL; }
						convertedChars = newsize = origsize = 0;
				} while (FindNextFile(hFind, &data));
				if (nstring) { delete[] nstring; nstring = NULL; }
			} else return false;
							
			//  LIMPIAR RECURSOS
			FindClose(hFind);
			if (wcstring) { delete[] wcstring; wcstring = NULL; }
			
			return true;
		}
        
#else // LINUX & MAC
        
        bool ListFilesUNIX(CoCStr_t path, Vfile_t& files)
        {
            DIR *pdir;
            struct dirent *pent;
            struct stat st;

            pdir=opendir(path);
            if (!pdir) return false;
            
            file_t hfile;
            std::string lpath;
            while ((pent=readdir(pdir)))
            {
                lpath.clear();
                lpath = path;
                if (lpath[lpath.size()-1] != htmk::utils::GetDirSep())
                    lpath.append(1, htmk::utils::GetDirSep());
                lpath.append(pent->d_name);
                memset(&st, 0, sizeof(struct stat));
                lstat(lpath.c_str(), &st);
                if(S_ISDIR(st.st_mode))
                    hfile.tipo = "D";
                else
                    hfile.tipo = "F";
                hfile.name = pent->d_name;
                if (hfile.name.compare(".") == 0)
                    continue;
                if (hfile.name.compare("..") != 0 && hfile.name[0] == '.')
                    continue;
                hfile.ruta = path;
                if (hfile.name.compare("..") != 0)
                {
                    Char_t lastc = hfile.ruta[hfile.ruta.size()-1];
                    if (lastc != htmk::utils::GetDirSep())
                        hfile.ruta.append(1, htmk::utils::GetDirSep());
                    hfile.ruta.append(hfile.name);
                    lastc = '\0';
                    lastc = hfile.ruta[hfile.ruta.size()-1];
                    if (lastc != htmk::utils::GetDirSep() && hfile.tipo.compare("D") == 0)
                        hfile.ruta.append(1, htmk::utils::GetDirSep());
                }
                else if (hfile.name.compare("..") == 0)
                {
                    hfile.tipo = "R";
                }
                files.push_back(hfile);
                
            }

            closedir(pdir);
            
            return true;
        }
        
#endif			
	}
}
